'''
Created on 29-01-2012

@author: Michal Golinski, Kamil Litwiniuk
'''
import random
import math
import time
from SimPy.Simulation import *
from collections import deque


OUTPUT_SPEED = 1000000
SIMULATION_TIME= 100

class Host(Process):
    '''
    classdocs
    '''
    
    def __init__(self, identifier, numberOfFlows, router, defaultFlowPath, maxPacketSize, packetSpeed, defaultTos, defaultPacketRandomness):
        '''
        Constructor
        '''
        Process.__init__(self, name='Host' + str(identifier))
        
        self.identifier = identifier        
        self.numberOfFlows = numberOfFlows                 
        self.router = router                
        
        self.flows = []
        self.outputBuffer = deque([])
        
        self.outputMonitor = OutputMonitor()
        
        self.createFlows(defaultFlowPath, maxPacketSize, packetSpeed, defaultTos, defaultPacketRandomness)
        self.startFlows()

        
    def createFlows(self, defaultFlowPath, maxPacketSize, packetSpeed, defaultTos, defaultPacketRandomness):
        for number in range(self.numberOfFlows):
            flow = Flow(self, number, defaultFlowPath, maxPacketSize, packetSpeed, defaultTos, defaultPacketRandomness)
            self.flows.append(flow)
    
    def startFlows(self):
        for flow in self.flows:
            activate(flow, flow.generateMessages(SIMULATION_TIME))
            
    def sendPackets(self, finishTime):
        while now() < finishTime:
            
            if (len(self.outputBuffer) <= 0 ):
                yield passivate, self
            else:
                msgToSend = self.outputBuffer.popleft()
                self.outputMonitor.monitorMessage(msgToSend)
                self.router.receiveEvent.signal(msgToSend)
                yield hold, self, float(msgToSend.size)/random.gauss((OUTPUT_SPEED*10), math.sqrt(OUTPUT_SPEED*10))


class OutputMonitor:
    
    def __init__(self):      
        self.monitors = dict()
        
    def monitorMessage(self, message):
        flowLabel = message.getFlowLabel()
        
        if (not self.monitors.has_key(flowLabel)):
            self.monitors[flowLabel] = Tally(name='Flow Monitor ' + flowLabel)
        
        messageMonitor = self.monitors.get(flowLabel)
        messageMonitor.observe(message.size)
  
               

class Flow(Process):
    '''
    classdocs
    '''
    
    def __init__(self, parentHost, identifier, path, maxSize, speed, typeOfService, packetRandomness):
        '''
        Constructor
        '''
        Process.__init__(self, name='Flow ' + str(identifier) + ' H' + str(parentHost.identifier))
        
        self.parentHost = parentHost
        self.identifier = identifier        #number
        self.path = path                    #string with specified format
        self.maxSize = maxSize              #maxSize of a packets created (bits)
        self.speed = speed                  #speed (packets per second)
        self.packetRandomness = packetRandomness
        
    def generateMessages(self, finishTime):
        while now() < finishTime:
            
            if self.packetRandomness == "random":
                randomSize = random.randint(1, self.maxSize)
            elif self.packetRandomness == "const":
                randomSize = self.maxSize
            elif self.packetRandomness == "bimodal":
                randomSize = self.bimodalDistribution(1, self.maxSize, self.maxSize/2, self.maxSize, self.maxSize*2, self.maxSize*3/2)
                
            msg = Message(randomSize, self.parentHost.identifier,  self.identifier, self.path)
            
            self.parentHost.outputBuffer.append(msg)
            
            if self.parentHost.passive():
                reactivate(self.parentHost)
            #print "Message created at", now(), msg.description(), "Stop flow", self.identifier, "at", now(), "for", 1.0/self.speed
            yield hold, self, 1.0/self.speed
            
    def bimodalDistribution(self, low1, high1, mode1, low2, high2, mode2 ):
        toss = random.choice( (1, 2) )
        if toss == 1:
            return random.triangular( low1, high1, mode1 ) 
        else:
            return random.triangular( low2, high2, mode2 )


class Message():
    def __init__(self, size, hostId, flowId, path):
        self.size = size
        self.hostId = hostId
        self.flowId = flowId
        self.path = path
        self.timestamp = time.time()



    def description(self):
        description = "Message:H{0}F{1} P:{2} S:{3} TS:{4}".format(self.hostId, self.flowId, self.path, self.size, self.timestamp)
        return description   
   
    def getFlowLabel(self):
        return "H" + str(self.hostId) + "F" + str(self.flowId)
    
    def __repr__(self):
        return self.description()
    
    def __str__(self):
        return self.description()